# app.py

import os
import logging
from flask import Flask, request, jsonify, render_template
from models import db, WebhookEvent, InvalidCommit
from config import Config
from datetime import datetime
import re
import subprocess

app = Flask(__name__)
app.config.from_object(Config)

# 初始化数据库
db.init_app(app)

# 设置日志记录
if not os.path.exists('logs'):
    os.makedirs('logs')

logging.basicConfig(
    filename=Config.LOG_FILE,
    level=logging.INFO,
    format='[%(asctime)s] %(levelname)s: %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

logger = logging.getLogger(__name__)

# DCO 正则表达式
DCO_REGEX = re.compile(r'^Signed-off-by:\s+([^<]+)<([^>]+)>$', re.IGNORECASE)

def send_email(subject, body, recipient_email):
    """
    发送邮件通知
    使用命令行调用邮件发送脚本
    """
    if not recipient_email:
        logger.warning('未指定邮件接收人，跳过发送邮件。')
        return

    # 调用 send_email.py 异步发送邮件
    cmd = [
        'python', 'send_email.py',
        '--subject', subject,
        '--body', body,
        '--recipient', recipient_email
    ]
    try:
        subprocess.Popen(cmd)
        logger.info(f'发送邮件通知给 {recipient_email}')
    except Exception as e:
        logger.error(f'发送邮件失败: {e}')

@app.route('/')
def index():
    query = request.args.get('query')
    if query:
        results = WebhookEvent.query.filter(WebhookEvent.repository_name.contains(query) | WebhookEvent.pusher.contains(query)).all()
    else:
        results = WebhookEvent.query.all()
    return render_template('index.html', results=results)

@app.route('/webhook', methods=['POST'])
def webhook():
    headers = request.headers
    signature = headers.get('X-Atomgit-Token', '')

    # 验证签名
    if Config.WEBHOOK_SECRET and signature != Config.WEBHOOK_SECRET:
        logger.warning('Webhook 签名验证失败。')
        return jsonify({'message': 'Unauthorized'}), 401

    event_type = headers.get('X-Atomgit-Event', '')
    logger.info(f'处理事件类型: {event_type}')

    if event_type.lower() != 'push':
        logger.info('事件类型不处理。')
        return jsonify({'message': 'Event not handled'}), 200

    data = request.get_json()
    if not data:
        logger.error('解析 Webhook 负载失败：无有效 JSON。')
        return jsonify({'message': 'Invalid payload'}), 400

    logger.debug(f'Webhook Payload: {data}')

    # 获取推送信息
    repository = data.get('repository', {})
    owner = repository.get('owner', {}).get('name', '')
    repo = repository.get('name', '')
    commits = data.get('commits', [])
    pusher = commits[0].get('author', {}).get('name', '') if commits else ''
    ref = data.get('ref', '')
    before = data.get('before', '')
    after = data.get('after', '')

    if not owner or not repo or not commits:
        logger.error('缺少必要的仓库或提交信息。')
        return jsonify({'message': 'Missing information'}), 400

    logger.info(f'处理仓库: {owner}/{repo}, Pusher: {pusher}, Ref: {ref}')

    # 插入 webhook_events 表
    event = WebhookEvent(
        repository_name=repo,
        repository_owner=owner,
        pusher=pusher,
        ref=ref,
        before_sha=before,
        after_sha=after,
        received_at=datetime.utcnow()
    )
    db.session.add(event)
    db.session.commit()
    logger.debug(f'插入 webhook_events ID: {event.id}')

    invalid_commits = []

    for commit in commits:
        message = commit.get('message', '')
        sha = commit.get('id', '')
        author = commit.get('author', {}).get('name', '')
        url = commit.get('url', '')

        logger.debug(f'验证提交 {sha} 的 DCO 签名。')

        has_dco = False
        lines = message.split('\n')
        for line in lines:
            if DCO_REGEX.match(line.strip()):
                has_dco = True
                logger.debug('找到有效的 DCO 签名。')
                break

        if not has_dco:
            logger.info(f'提交 {sha} 由 {author} 创建，未通过 DCO 验证。')
            invalid_commit = InvalidCommit(
                sha=sha,
                author=author,
                message=message,
                url=url,
                detected_at=datetime.utcnow(),
                event_id=event.id
            )
            db.session.add(invalid_commit)
            invalid_commits.append(invalid_commit)

    if invalid_commits:
        db.session.commit()  # 提交 invalid_commits
        subject = Config.EMAIL_SUBJECT or f"DCO 验证失败 - 仓库 {owner}/{repo} 的新提交"
        body = Config.EMAIL_BODY_TEMPLATE or "以下提交缺少有效的 `Signed-off-by` 行，请在每次提交时使用 `git commit -s` 添加签名：\n\n"

        for commit in invalid_commits:
            body += f"- **Commit SHA**: {commit.sha}\n"
            body += f"  - **作者**: {commit.author}\n"
            body += f"  - **消息**: {commit.message}\n"
            body += f"  - **链接**: {commit.url}\n\n"

        # 发送邮件通知
        send_email(subject, body, Config.EMAIL_RECIPIENT)

        logger.info(f'发现 {len(invalid_commits)} 个未通过 DCO 验证的提交，已发送邮件通知。')
    else:
        logger.info('所有提交均通过 DCO 验证。')

    return jsonify({'message': 'Processed'}), 200


@app.route('/view_events', methods=['GET'])
def view_events():
    # 获取查询参数
    repository = request.args.get('repository', '').strip()
    owner = request.args.get('owner', '').strip()
    pusher = request.args.get('pusher', '').strip()
    date_from = request.args.get('date_from', '').strip()
    date_to = request.args.get('date_to', '').strip()
    invalid_commits_filter = request.args.get('invalid_commits', '').strip()  # 新增

    query = WebhookEvent.query

    if repository:
        query = query.filter(WebhookEvent.repository_name.ilike(f'%{repository}%'))
    if owner:
        query = query.filter(WebhookEvent.repository_owner.ilike(f'%{owner}%'))
    if pusher:
        query = query.filter(WebhookEvent.pusher.ilike(f'%{pusher}%'))
    if date_from:
        try:
            date_from_dt = datetime.strptime(date_from, '%Y-%m-%d')
            query = query.filter(WebhookEvent.received_at >= date_from_dt)
        except ValueError:
            logger.warning(f'日期格式错误: {date_from}')
    if date_to:
        try:
            date_to_dt = datetime.strptime(date_to, '%Y-%m-%d')
            query = query.filter(WebhookEvent.received_at <= date_to_dt)
        except ValueError:
            logger.warning(f'日期格式错误: {date_to}')

    # 处理新的无效提交筛选
    if invalid_commits_filter == 'yes':
        query = query.join(WebhookEvent.invalid_commits).filter(InvalidCommit.id.isnot(None)).distinct()
    elif invalid_commits_filter == 'no':
        query = query.outerjoin(WebhookEvent.invalid_commits).filter(InvalidCommit.id.is_(None)).distinct()

    events = query.order_by(WebhookEvent.received_at.desc()).all()
    
    # 准备筛选条件以保持在表单中
    filters = {
        'repository': repository,
        'owner': owner,
        'pusher': pusher,
        'date_from': date_from,
        'date_to': date_to,
        'invalid_commits': invalid_commits_filter
    }

    logger.info(f'展示事件数量: {len(events)}')

    return render_template('view_events.html', events=events, filters=filters)

@app.route('/env_test')
def env_test():
    return jsonify({
        'DATABASE_URI': Config.SQLALCHEMY_DATABASE_URI,
        'WEBHOOK_SECRET': Config.WEBHOOK_SECRET,
        'EMAIL_RECIPIENT': Config.EMAIL_RECIPIENT
    })

@app.errorhandler(404)
def page_not_found(e):
    logger.warning(f'页面未找到: {request.url}')
    return render_template('404.html'), 404

@app.errorhandler(500)
def internal_server_error(e):
    logger.error(f'内部服务器错误: {e}')
    return render_template('500.html'), 500

if __name__ == '__main__':
    # 在首次运行时创建数据库表
    with app.app_context():
        db.create_all()
    # 运行 Flask 应用
    app.run(host='0.0.0.0', port=5000, debug=True)
